#include <kuai/db/dbtool.hpp>

namespace kuai {
	
	DatabaseJob::DatabaseJob(int mode) {
		open_mode = mode;
	}

	void DatabaseJob::doJob(std::ostream& out_log, std::ostream& err_log, StringMap& par) {
		FileName file(getParameter(par, "database", String("")));
		if (file.empty()) {
			throw error("You have not assign the database.");
		}
		else if (!file.is_file() && (open_mode & SQLITE_OPEN_CREATE) == 0) {
			throw error("%1% is not a file.", file);
		}

		pdb = Sqlite3Database::create(file, open_mode);
		
		String synchronous = getParameter(par, "synchronous", "");
		if (!synchronous.empty()) {
			pdb->execute(("PRAGMA synchronous = " + synchronous).c_str());
		}

		size_t cache = getParameter(par, "cache_size", size_t(0));
		if (cache != 0) {
			pdb->execute(("PRAGMA cache_size = " + str(cache)).c_str());
		}

		String jmode = getParameter(par, "journal_mode", "");
		if (!jmode.empty()) {
			pdb->execute(("PRAGMA journal_mode = " + jmode).c_str());
		}

		bool useTransaction = getParameter(par, "transaction", true);
		if (useTransaction) {
			pdb->begin();
		}

		doJob(pdb, out_log, err_log, par);

		if (useTransaction) {
			pdb->commit();
		}

		pdb.reset();
	}

	void DatabaseJob::dumpTable(const String& table, std::ostream& stream, const String& sep) {
		const String sql = "SELECT * FROM " + table;
		Sqlite3StatementPointer  p = pdb->prepare(sql);
		dumpTable(*p, stream, sep);
	
	}
	void DatabaseJob::dumpTable(Sqlite3Statement& table, std::ostream& stream, const String& sep) {
		size_t n = table.countColumn();
		for (size_t i = 0; i < n; ++i) {
			stream << table.columnName(i) << sep;
		}
		stream << std::endl << std::endl;
		while (table.step() == SQLITE_ROW) {
			for (size_t i = 0; i < n; ++i) {
				stream << table.column(i) << sep;
			}
			stream << std::endl;
		}
	}

	void DatabaseJob::dumpTable(const String& table, const FileName& file, const String& sep) {
		std::ofstream stream(file.c_str());
		dumpTable(table, stream, sep);
	}
	void DatabaseJob::dumpTable(Sqlite3Statement& table, const FileName& file, const String& sep) {
		std::ofstream stream(file.c_str());
		dumpTable(table, stream, sep);
	}

	void DatabaseJob::help(std::ostream& output) {
		output << 
			"    DATABASE = database.db\n"
			"        database.db is the name of database\n"
			"    SYNCHRONOUS = 0 | OFF | 1 | NORMAL | 2 | FULL\n"
			"        Setting of the synchronous flag. When synchronous is FULL (2), the\n"
			"        SQLite database engine will pause at critical moments to make sure\n"
			"        that data has actually been written to the disk surface before continu-\n"
			"        ing. This ensures that if the operating system crashes or if there is a\n"
			"        power failure, the database will be uncorrupted after rebooting. FULL\n"
			"        synchronous is very safe, but it is also slower. When synchronous is\n"
			"        NORMAL (1), the SQLite database engine will still pause at the most\n"
			"        critical moments, but less often than in FULL mode. There is a very\n"
			"        small (though non-zero) chance that a power failure at just the wrong\n"
			"        time could corrupt the database in NORMAL mode. But in practice, you\n"
			"        are more likely to suffer a catastrophic disk failure or some other\n"
			"        unrecoverable hardware fault. With synchronous OFF (0), SQLite\n"
			"        continues without pausing as soon as it has handed data off to the\n"
			"        operating system. If the application running SQLite crashes, the data\n"
			"        will be safe, but the database might become corrupted if the operating\n"
			"        system crashes or the computer loses power before that data has been\n"
			"        written to the disk surface. On the other hand, some operations are as\n"
			"        much as 50 or more times faster with synchronous OFF. \n"
			"    CACHE_SIZE = n\n"
			"        n is the number-of-pages. It changes the suggested maximum number of\n"
			"        database disk pages that SQLite will hold in memory at once per open\n"
			"        database file. Whether or not this suggestion is honored is at the\n"
			"        discretion of the Application Defined Page Cache. In the default page\n"
			"        cache implemention, the suggested cache size is honored as long as it\n"
			"        is 10 or greater. A suggested cache size of less than 10 are treated as\n"
			"        if it were 10. Alternative application-defined page cache implementa-\n"
			"        tions may choose to interpret the suggested cache size in different ways\n"
			"        or to ignore it all together. The default suggested cache size is 2000.\n"
			"        When you change the cache size using the cache_size pragma, the change\n"
			"        only endures for the current session. The cache size reverts to the\n"
			"        default value when the database is closed and reopened.\n"
			"    JOURNAL_MODE = DELETE | TRUNCATE | PERSIST | MEMORY | OFF\n"
			"        It changes the journaling mode. The DELETE journaling mode is the normal\n"
			"        behavior. In the DELETE mode, the rollback journal is deleted at the\n"
			"        conclusion of each transaction. Indeed, the delete operation is the\n"
			"        action that causes the transaction to commit. (See the documented titled\n"
			"        Atomic Commit In SQLite for additional detail.) The TRUNCATE journaling\n"
			"        mode commits transactions by truncating the rollback journal to zero-\n"
			"        length instead of deleting it. On many systems, truncating a file is\n"
			"        much faster than deleting the file since the containing directory does\n"
			"        not need to be changed. The PERSIST journaling mode prevents the roll-\n"
			"        back journal from being deleted at the end of each transaction. Instead,\n"
			"        the header of the journal is overwritten with zeros. This will prevent\n"
			"        other database connections from rolling the journal back. The PERSIST\n"
			"        journaling mode is useful as an optimization on platforms where deleting\n"
			"        or truncating a file is much more expensive than overwriting the first\n"
			"        block of a file with zeros. The MEMORY journaling mode stores the roll-\n"
			"        back journal in volatile RAM. This saves disk I/O but at the expense of\n"
			"        database safety and integrity. If the application using SQLite crashes\n"
			"        in the middle of a transaction when the MEMORY journaling mode is set,\n"
			"        then the database file will very likely go corrupt. The OFF journaling\n"
			"        mode disables the rollback journal completely. No rollback journal is\n"
			"        ever created and hence there is never a rollback journal to delete. The\n"
			"        OFF journaling mode disables the atomic commit and rollback capabilities\n"
			"        of SQLite. The ROLLBACK command no longer works; it behaves in an un-\n"
			"        defined way. Applications must avoid using the ROLLBACK command when the\n"
			"        journal mode is OFF. If the application crashes in the middle of a trans\n"
			"        action when the OFF journaling mode is set, then the database file will\n"
			"        very likely go corrupt. Note that the journal_mode for an in-memory\n"
			"        database is either MEMORY or OFF and can not be changed to a different\n"
			"        value. An attempt to change the journal_mode of an in-memory database to\n"
			"        any setting other than MEMORY or OFF is ignored. Note also that the\n"
			"        journal_mode cannot be changed while a transaction is active.\n"
			"    TRANSACTION = TRUE | FALSE\n"
			"        Whether use transaction mode. In transaction mode a set of sql query\n"
			"        will not take effect until it meet COMMIT command. On the other hand\n"
			"        if you trun TRANSACTION off then each single query will treat as a\n"
			"        transaction. It is far more slow than the transaction mode.\n"
		;
	};


	IMPLEMENT_JOB_CREATOR(DumpTableJob, dump_table);

	DumpTableJob::DumpTableJob()
		: DatabaseJob(SQLITE_OPEN_READONLY)
	{ }

	void DumpTableJob::doJob(Sqlite3DatabasePointer pdb, std::ostream& out_log, std::ostream& err_log, StringMap& par) 
	{
		String sep = getParameter(par, "sep", "|");
		for (StringMap::const_iterator
			it = par.begin(); it != par.end(); ++it)
		{
			out_log << "Dumping " << it->first << std::endl;
			dumpTable(it->first, FileName(it->second), sep);
		}
		par.clear();
	}

	JOB_HELP_FUNCTION(DumpTableJob, output) {
		output << 
			"This function dump the table contents into a file.\n"
			"Parameters:\n";
		DatabaseJob::help(output);
		output << 
			"    tablename=output_file.txt\n"
			"        tablename is the name of the table you want to dump.\n"
			"        output_file.txt is the file name of target\n"
		;
	}
}
